/*
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * openGauss is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 * http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * ---------------------------------------------------------------------------------------
 *
 * imcs_am.h
 * routines to support ColStore
 *
 *
 * IDENTIFICATION
 * src/include/access/imcs/imcs_am.h
 *
 * ---------------------------------------------------------------------------------------
 */

#ifndef IMCSAM_H
#define IMCSAM_H

#include "imcs_roughcheck_func.h"
#include "imcs_minmax_func.h"
#include "imcs.h"
#include "imcu.h"
#include "imcu_storage.h"
#include "imcu_cache_mgr.h"
#include "utils/snapshot.h"
#ifdef ENABLE_HTAP
#include "storage/htap/htap_delta_table.h"
#include "storage/htap/htap_delete_map.h"
#else
#include "imcs_ctlg.h"
#endif


#define MAX_IMCU_PREFETCH_REQSIZ (64)

#ifndef ENABLE_HTAP
#define IM_Max_Del_Bitmap_Size ((int)IMCS_MAX_ROWS_SIZE / 8 + 1)
#endif

/*
 * The IMCS stores extra ctid. The 0th col during IMCU scanning is reserved for ctid
 */
#define IMCS_CTID_IDX 0
#define IMCS_CTID_ATTNUM 0
#define IMCS_CTID_ATTR_TYPE TIDOID
#define IMCS_CTID_ATTR_NAME "ctid"
#define IMCS_CTID_ATTR_MOD -1

class IMCSBatchCUData : public BaseObject {
public:
    IMCUDesc **CUDescData;
    IMCU **CUptrData;
    char *bitmap; /* The delete bitmap that we would keep for the IMCU that's been moved */
    int colNum;
    bool hasValue;

    IMCSBatchCUData()
    {
        CUDescData = NULL;
        CUptrData = NULL;
        bitmap = NULL;
        colNum = 0;
        hasValue = false;
    }

    virtual ~IMCSBatchCUData() {}

    void Init(const int &relColNum)
    {
        colNum = relColNum;
        CUDescData = (IMCUDesc **)palloc0(colNum * sizeof(IMCUDesc *));
        CUptrData = (IMCU **)palloc0(colNum * sizeof(IMCU *));
        for (int i = 0; i < colNum; ++i) {
            CUDescData[i] = New(CurrentMemoryContext)IMCUDesc;
        }
    }

    void CopyDelMask(unsigned char *cu_del_mask)
    {
        bitmap = (char *)cu_del_mask;
    }

    bool batchCUIsNULL()
    {
        return !hasValue;
    }

    void reset()
    {
        hasValue = false;
    }
    virtual void Destroy()
    {
        for (int i = 0; i < colNum; ++i) {
            DELETE_EX(CUDescData[i]);
        }
        pfree_ext(CUDescData);
        pfree_ext(CUptrData);
    }
};

/*
 * If we load all IMCUDesc, the memory will be huge,
 * So we define this data structure defining the load IMCUDesc information
 */
struct IMCSLoadCuDescCtl : public BaseObject {
    uint32 curLoadNum;
    uint32 last_load_num;
    uint32 next_cuid;
    uint32 last_load_cu;
    bool rg_scan_completed;
    bool locked;
    IMCUDesc *cuDescArray;

    IMCSLoadCuDescCtl(uint32 start_cuid)
    {
        Reset(start_cuid);
        cuDescArray = (IMCUDesc *)palloc0(sizeof(IMCUDesc) * u_sess->attr.attr_storage.max_loaded_cudesc);
    }

    virtual ~IMCSLoadCuDescCtl() {}

    virtual void Destroy()
    {
        if (cuDescArray != NULL) {
            pfree(cuDescArray);
            cuDescArray = NULL;
        }
    }

    inline bool HasFreeSlot()
    {
        return curLoadNum < (uint32)u_sess->attr.attr_storage.max_loaded_cudesc;
    }

    inline void Reset(uint32 start_cuid)
    {
        curLoadNum = 0;
        last_load_num = 0;
        next_cuid = start_cuid;
        last_load_cu = -1;
        rg_scan_completed = false;
        locked = false;
    }
};

struct IMCSScanState;
typedef IMCSScanState *IMCStoreScanDesc;
typedef CStoreScanKey IMCSScanKey;


/*
 * IMCStore include a set of common API for ColStore.
 * In future, we can add more API.
 */
class IMCStore : public BaseObject {
    /* public static area */
public:
    /* create data files */
    static void CreateStorage(Relation rel, Oid new_rel_file_node = InvalidOid);

    /* form and deform VC IMCU Desc tuple.
     * We add a virtual column for marking deleted rows.
     * The VC is divided into CUs.
     */
    static HeapTuple FormVCCUDescTup(_in_ TupleDesc cudesc, _in_ const char *del_mask, _in_ uint32 cuid,
        _in_ int32 row_count, _in_ uint32 magic);

    static void SaveVCCUDesc(_in_ Oid cudesc_oid, _in_ uint32 cuid, _in_ int row_count, _in_ uint32 magic,
        _in_ int options, _in_ const char *del_bitmap = NULL);

    static bool SetCudescModeForMinMaxVal(_in_ bool full_nulls, _in_ bool has_min_max_func, _in_ bool has_null,
        _in_ int max_var_str_len, _in_ int attlen, __inout IMCUDesc *cu_desc_ptr);

    static bool SetCudescModeForTheSameVal(_in_ bool full_nulls, _in_ ImcsFuncSetMinMax SetMinMaxFunc, _in_ int attlen,
        _in_ Datum attVal, __inout IMCUDesc *cu_desc_ptr);

    uint32 GetMaxCUID();

    static CUPointer GetMaxCUPointer(_in_ int attrno, _in_ Relation rel);

public:
    IMCStore();
    virtual ~IMCStore();
    virtual void Destroy();

    /* Scan APIs */
    void InitScan(IMCSScanState *state, Snapshot snapshot = NULL);
    void InitReScan();
    void InitPartReScan(Relation rel);
    bool IsEndScan() const;

    /* late read APIs */
    bool IsLateRead(int id) const;
    void ResetLateRead();

    /* update cstore scan timing flag */
    void SetTiming(IMCSScanState *state);

    /* Load IMCUDesc information of column according to load_info_ptr
     * LoadCUDescCtrl include maxCUDescNum for this load, because if we load all
     * it need big memory to hold
     */
    bool LoadCUDesc(_in_ int col, __inout IMCSLoadCuDescCtl *load_cu_desc_info_ptr, _in_ bool prefetch_control);

    void GetCtidImcuData(_in_ IMCUDesc *cuDescPtr);
    /* Get tuple deleted information from VC IMCU description. */
    void GetCUDeleteMaskIfNeed(_in_ IMCUDesc *ctid_cu_desc, _in_ Snapshot snap_shot);

    /* Get IMCU data.
     * Note that the IMCU is pinned
     */
    IMCU *GetCUData(_in_ IMCUDesc *cu_desc_ptr, _in_ int col_idx, _in_ int val_size, _out_ int &slot_id);

    IMCU *GetUnCompressCUData(Relation rel, int col, uint32 cuid, _out_ int &slot_id,
        ForkNumber fork_num = MAIN_FORKNUM, bool enter_cache = true) const;

    /* Fill Vector APIs */
    int FillVecBatch(_in_ IMCSScanState *state, _in_ int maxRowsToFill, _out_ VectorBatch *vec_batch_out);

    /* Fill Vector of column */
    template <bool has_dead_row, int attlen>
    int FillVector(_in_ int col_idx, _in_ IMCUDesc *cu_desc_ptr, _in_ int maxRowsToFill, _out_ ScalarVector *vec);

    template <int attlen>
    void FillVectorLateRead(_in_ int seq, _in_ ScalarVector *tids, _in_ IMCUDesc *cu_desc_ptr, _out_ ScalarVector *vec);

    /* Fill system column into ScalarVector */
    int FillSysColVector(_in_ int col_idx, _in_ IMCUDesc *cu_desc_ptr, _in_ int maxRowsToFill, _out_ ScalarVector *vec);

    template <int sys_col_oid> void FillSysVecByTid(_in_ ScalarVector *tids, _out_ ScalarVector *dest_vec);

    template <bool has_dead_row>
    int FillTidForLateRead(_in_ IMCUDesc *cu_desc_ptr, _in_ int maxRowsToFill, _out_ ScalarVector *vec);

    void FillScanBatchLateIfNeed(__inout VectorBatch *vec_batch);

    /* Set IMCU range for scan in redistribute. */
    void SetScanRange();

    /* Judge whether dead row */
    bool IsDeadRow(uint32 cuid, uint32 row) const;

    void CUListPrefetch();
    void CUPrefetch(IMCUDesc *cudesc, int col, AioDispatchCUDesc_t **d_list, int &count, File *vfd_list);

    /* Point to scan function */
    typedef void (IMCStore::*ScanFuncPtr)(_in_ IMCSScanState *state, _in_ int maxRowsToFill,
        _out_ VectorBatch *vec_batch_out);
    void RunScan(_in_ IMCSScanState *state, _in_ int maxRowsToFill, _out_ VectorBatch *vec_batch_out);
    int GetNextCu(void) const;
    int GetLastLoadCu() const;
    int SetLastLoadCu(uint32 cu_id);
    bool IsLocked();
    bool setLocked(bool locked);
    void CopyVecByView(_in_ VectorBatch *src_batch, _out_ VectorBatch *dest_batch, int start_idx, int end_idx,
        bool deep_copy);
#ifdef ENABLE_HTAP
    void runDeltaScan(_in_ IMCSScanState *state, _in_ int maxRowsToFill, _out_ VectorBatch *vec_batch_out);
    bool checkDeltaStatus();
#endif

    int GetLateReadCtid() const;
    void IncLoadCuDescCursor();

    int GetColNum();
    int *GetColId();

public: /* public vars */
    /* Inserted/Scan Relation */
    Relation m_relation;

private: /* private methods. */
#ifdef ENABLE_HTAP
    bool initHTAPDelta(IMCSScanState *state);
    bool DeltaReset();
#endif

    /* IMCStore scan : pass vector to VE. */
    void IMCSScan(IMCSScanState *state, int maxRowsToFill, VectorBatch *vec_batch_out);
    void ImcsMinMaxScan(IMCSScanState *state, int maxRowsToFill, VectorBatch *vec_batch_out);

    /* The number of holding IMCUDesc is  max_loaded_cudesc
     * if we load all IMCUDesc once, the memory will not enough.
     * So we load CUdesc once for max_loaded_cudesc
     */
    void LoadCUDescIfNeed();

    /* Do rough_check if need
     * elimiate IMCU by min/max value of IMCU.
     */
    void RoughCheckIfNeed(_in_ IMCSScanState *state);

    /* Refresh cursor */
    void RefreshCursor(int row, int dead_rows);

    void InitRoughCheckEnv(IMCSScanState *state);

    void BindingFp(IMCSScanState *state);
    void InitFillVecEnv(IMCSScanState *state);
    void InitCtidAttr();

    /* indicate whether only accessing system column or const column.
     * true, means that m_virtualCUDescInfo is a new and single object.
     * false, means that m_virtualCUDescInfo just a pointer to m_CUDescInfo[0].
     */
    inline bool OnlySysOrConstCol(void)
    {
        // return ((m_colNum == 1 && m_sysColNum != 0) || m_onlyConstCol);
        return false;
    }

    int LoadCudescMinus(int start, int end) const;
    bool HasEnoughCuDescSlot(int start, int end) const;
    bool NeedLoadCUDesc(int32 &cudesc_idx);
    void IncLoadCuDescIdx(int &idx) const;
    bool RoughCheck(IMCSScanKey scanKey, int nkeys, int cu_desc_idx);

    void FillColMinMax(IMCUDesc *cu_desc_ptr, ScalarVector *vec, int pos);

    /* only called by GetCUData() */
    CUUncompressedRetCode GetCUDataFromRemote(IMCUDesc *cu_desc_ptr, IMCU *cu_ptr, int col_idx, int val_size,
        const int &slot_id);

    /* defence functions */
    void CheckConsistenceOfCUDescCtl(void);
    void CheckConsistenceOfCUDesc(int cudesc_idx) const;
    void CheckConsistenceOfCUData(IMCUDesc *cu_desc_ptr, IMCU *cu, AttrNumber col) const;

private:
    /* control private memory used locally.
     * m_scanMemContext: for objects alive during the whole cstore-scan
     * m_perScanMemCnxt: for memory per heap table scan and temp space
     * during decompression.
     */
    MemoryContext m_scanMemContext;
    MemoryContext m_perScanMemCnxt;

    /* current snapshot to use. */
    Snapshot m_snapshot;

    /* 1. Accessed user column id
     * 2. Accessed system column id
     * 3. flags for late read
     * 4. each IMCU storage fro each user column.
     */
    int *m_colId;
    int *m_sysColId;
    bool *m_lateRead;
    IMCUStorage **m_cuStorage;

    /* 1. The IMCUDesc info of accessed columns
     * 2. virtual IMCUDesc for sys or const columns
     */
    IMCSLoadCuDescCtl **m_CUDescInfo;
    IMCSLoadCuDescCtl *m_virtualCUDescInfo;

    /* Accessed IMCUDesc index array
     * After rough_check, which IMCU will be accessed
     */
    int *m_CUDescIdx;

    /* adio param */
    int m_lastNumCUDescIdx;
    int m_prefetch_quantity;
    int m_prefetch_threshold;
    bool m_load_finish;

    /* Current scan position inside IMCU
     */
    int *m_scanPosInCU;

    /* Rough Check Functions
     */
    ImcsRoughCheckFunc *m_RCFuncs;

    typedef int (IMCStore::*m_colFillFun)(int seq, IMCUDesc *cu_desc_ptr, int maxRowsToFill, ScalarVector *vec);

    typedef struct {
        m_colFillFun colFillFun[2];
    } colFillArray;

    typedef void (IMCStore::*FillVectorByTidsFun)(_in_ int col_idx, _in_ ScalarVector *tids, _out_ ScalarVector *vec);

    typedef void (IMCStore::*FillVectorLateReadFun)(_in_ int seq, _in_ ScalarVector *tids, _in_ IMCUDesc *cu_desc_ptr,
        _out_ ScalarVector *vec);

    FillVectorByTidsFun *m_fillVectorByTids;
    FillVectorLateReadFun *m_fillVectorLateRead;
    colFillArray *m_colFillFunArrary;

    typedef void (IMCStore::*fillMinMaxFuncPtr)(IMCUDesc *cu_desc_ptr, ScalarVector *vec, int pos);
    fillMinMaxFuncPtr *m_fillMinMaxFunc;

    ScanFuncPtr m_scanFunc; /* cstore scan function ptr */

    /* node id of this plan */
    int m_plan_node_id;

    /* 1. Number of accessed user columns
     * 2. Number of accessed system columns.
     */
    int m_colNum;
    int m_sysColNum;

    /* 1. length of loaded IMCUDesc info or virtual IMCUDesc info
     * 2. length of m_CUDescIdx
     */
    int m_NumLoadCUDesc;
    int m_NumCUDescIdx;

    /* 1. IMCU id of current deleted mask.
     * 2. Current access cursor in m_CUDescIdx
     * 3. Current access row cursor inside IMCU
     */
    uint32 m_delMaskCUId;
    int m_cursor;
#ifdef ENABLE_HTAP
    htap::HTAPDelta::ScanContext m_deltaScanCtx;
#endif
    int m_rowCursorInCU;

    uint32 m_startCUID; /* scan start IMCU ID. */
    uint32 m_endCUID;   /* scan end IMCU ID. */

#ifdef ENABLE_HTAP
    htap::HTAPDeleteMap *m_cuDelMask;
    htap::HTAPDelta *m_htap_delta; /* Delta flush helper */
#else
    unsigned char m_cuDelMask[IM_Max_Del_Bitmap_Size];
#endif

    /* whether dead rows exist */
    bool m_hasDeadRow;
    /* Is need do rough check */
    bool m_needRCheck;
    /* Only access const column */
    bool m_onlyConstCol;

    bool m_timing_on; /* timing IMCSScan steps */

    RangeScanInRedis m_rangeScanInRedis; /* if it is a range scan at redistribution time */

    /* cbtree index flag */
    bool m_useBtreeIndex;

    /* the first column index, start from 0 */
    int m_firstColIdx;

    /* for late read
     * the cuDesc id of batch in the cuDescArray.
     */
    int m_cuDescIdx;

    /* for late read
     * the first late read column idx which is filled with ctid.
     */
    int m_laterReadCtidColIdx;

    /* for ctid */
    IMCU *m_imcuCtidData;            /* save ctid data from imcu */
    FormData_pg_attribute m_ctid_attr; /* virtual attr for ctid */
};

void IMCStoreAbortCu();
void ImcsVerifyAbortCu();

extern void CheckUniqueOnOtherIdx(Relation index, Relation heap_rel, Datum *values, const bool *isnull);

#endif
